package com.rssreader.server.services;

import java.io.IOException;
import java.net.URL;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.Namespace;
import org.jdom2.input.SAXBuilder;

import com.extjs.gxt.ui.client.data.BasePagingLoadResult;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.PagingLoadConfig;
import com.extjs.gxt.ui.client.data.PagingLoadResult;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.rssreader.client.services.FeedService;
import com.rssreader.server.utils.FilePersistence;
import com.rssreader.server.utils.Persistence;
import com.rssreader.shared.model.Category;
import com.rssreader.shared.model.Feed;
import com.rssreader.shared.model.Item;

public class FeedServiceImpl extends RemoteServiceServlet implements
	FeedService {

    // 加入日誌
    private final static Logger LOGGER = Logger.getLogger(FeedServiceImpl.class
	    .getName());
    // 加入HashMap的屬性，用來儲存Feed物件
    private Map<String, Feed> feeds = new HashMap<String, Feed>();
    // 加入一個FilePersistence類別的實體物件
    private final Persistence persistence = new FilePersistence();

    @Override
    public Feed createNewFeed() {
	// generates a new UUID, creates a new Feed object with that UUID, and
	// returns it
	UUID uuid = UUID.randomUUID();
	return new Feed(uuid.toString());
    }

    @Override
    public void saveFeed(Feed feed) {
	Element eleRoot = new Element("rss");
	eleRoot.setAttribute(new Attribute("version", "2.0"));

	// Create a document from the feed object
	Document document = new Document(eleRoot);

	Element eleChannel = new Element("channel");
	Element eleTitle = new Element("title");
	Element eleDescription = new Element("description");
	Element eleLink = new Element("link");

	eleTitle.setText(feed.getTitle());
	eleDescription.setText(feed.getDescription());
	eleLink.setText(feed.getLink());

	eleChannel.addContent(eleTitle);
	eleChannel.addContent(eleDescription);
	eleChannel.addContent(eleLink);

	eleRoot.addContent(eleChannel);

	// try {
	// XMLOutputter serializer = new XMLOutputter();
	// Format prettyFormat = Format.getPrettyFormat();
	// serializer.setFormat(prettyFormat);
	// System.out
	// .println("At this point we would serialize the feed "
	// + feed.getTitle()
	// +
	// " to a file. For now we are just going to write it to the console.");
	// serializer.output(document, System.out);
	// } catch (IOException e) {
	// System.out.println("Error saving feed");
	// }

	// 調用Persistence介面的saveFeedXml方法，用於持久化的處理
	persistence.saveFeedXml(feed.getUuid(), document);
	// 將新生成的持久化xml文件的URL返回給addExistingFeed方法，將其保存到feed.txt列表裡
	addExistingFeed(persistence.getUrl(feed.getUuid()));
    }

    // 調用loadFeed方法，根據feedUrl返回的feed物件
    // 再調用持久畫類別中的saveFeedList方法，完成持久化操作
    @Override
    public void addExistingFeed(String feedUrl) {
	Feed loadResult = loadFeed(feedUrl, false);
	if (loadResult.getTitle() != null) {
	    feeds.put(feedUrl, loadResult); // 將原本loadFeed(feedUrl)替換為loadResult
	    persistence.saveFeedList(feeds.keySet());
	}
    }

    // 根據feed的URL，在JDOM規範下，解析從Internet裡獲得RSS的XML內容，並返回Feed物件的bean
    private Feed loadFeed(String feedUrl, boolean loadItems) {
	Feed feed = new Feed(feedUrl);
	try {
	    SAXBuilder parser = new SAXBuilder();
	    Document document = parser.build(new URL(feedUrl));
	    Element eleRoot = document.getRootElement();
	    Element eleChannel = eleRoot.getChild("channel");
	    // 檢索RSS feed XML中的image，若有則擷取圖片的URL並設定Feed物件的imageUrl欄位
	    Element eleImage = eleChannel.getChild("image");

	    feed.setTitle(eleChannel.getChildText("title"));
	    feed.setDescription(eleChannel.getChildText("description"));
	    feed.setLink(eleChannel.getChildText("link"));

	    feed.setImageUrl("");
	    if (eleImage != null) {
		Element eleUrl = eleImage.getChild("url");
		if (eleUrl != null)
		    feed.setImageUrl(eleUrl.getText());
	    }

	    // 若loadItems是true，讀取feed's items到Feed物件中的items欄位
	    if (loadItems)
		feed.setItems(loadItems(feedUrl));

	    return feed;
	} catch (IOException e) {
	    LOGGER.log(Level.SEVERE, "IO Error loading feed", e);
	    return feed;
	} catch (JDOMException e) {
	    LOGGER.log(Level.SEVERE, "Error parsing feed", e);
	    return feed;
	}
    }

    // 讀取feeds.txt文件裡所有的URL集合，將其存在client side的store中
    @Override
    public List<Feed> loadFeedList(boolean loadItems) {
	feeds.clear();
	// 調用loadFeedList方法，返回feed URL結果set
	Set<String> feedUrls = persistence.loadFeedList();
	// 瀏覽set，依據每個URL讀取其feed內容，儲存在HashMap feeds裡
	for (String feedUrl : feedUrls) {
	    feeds.put(feedUrl, loadFeed(feedUrl, loadItems)); // (URL, feed)
	}
	return new ArrayList<Feed>(feeds.values()); // 將值存入list回傳
    }

    // 根據feed的URL，在JDOM規範下，解析從Internet裡獲得多組的RSS的XML內容，並返回Feed物件的bean
    @Override
    public List<Item> loadItems(String feedUrl) {
	List<Item> items = new ArrayList<Item>();
	try {
	    SAXBuilder parser = new SAXBuilder();
	    Document document = parser.build(new URL(feedUrl));
	    Element eleRoot = document.getRootElement();
	    Element eleChannel = eleRoot.getChild("channel");
	    List<Element> itemElements = eleChannel.getChildren("item");
	    for (Element eleItem : itemElements) {
		Item item = new Item();
		item.setTitle(eleItem.getChildText("title"));
		item.setDescription(eleItem.getChildText("description"));
		item.setLink(eleItem.getChildText("link"));
		item.setCategory(eleItem.getChildText("category"));
		// 從RSS feed XML的item從擷取任何thumbnail，
		// 並設置Item物件的thumbnailUrl和pubDate欄位
		// Feed物件包含一圖片的URL
		Namespace ns = Namespace.getNamespace("media",
			"http://search.yahoo.com/mrss/");
		Element eleThumbnail = eleItem.getChild("thumbnail", ns);
		if (eleThumbnail != null) {
		    // 若圖片的url有用，則feed物件增加一個thumbnail的URL和一個publication date
		    item.setThumbnailUrl(eleThumbnail.getAttributeValue("url"));
		}
		String pubDateStr = eleItem.getChildText("pubDate");
		if (pubDateStr != null) {
		    try {
			DateFormat df = new SimpleDateFormat(
				"EEE', 'd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z");
			item.setPubDate(df.parse(pubDateStr));
		    } catch (ParseException e) {
			item.setPubDate(null);
		    }
		}
		items.add(item);
	    }
	    return items;
	} catch (IOException e) {
	    e.printStackTrace();
	    return items;

	} catch (JDOMException e) {
	    e.printStackTrace();
	    return items;
	}
    }

    // return either a list of Category objects or a List of Item objects
    // contained within a specified Category
    @Override
    public List<ModelData> loadCategorisedItems(String feedUrl,
	    Category category) {
	List<Item> items = loadItems(feedUrl);
	Map<String, List<Item>> categorisedItems = new HashMap<String, List<Item>>();
	for (Item item : items) {
	    // 0: get each item's category
	    String itemCategoryStr = item.getCategory();
	    if (itemCategoryStr == null) {
		itemCategoryStr = "Uncategorised";
	    }
	    // 1: get categoryItems by itemCategoryStr
	    List<Item> categoryItems = categorisedItems.get(itemCategoryStr);
	    if (categoryItems == null) {
		categoryItems = new ArrayList<Item>();
	    }
	    // 2: add current item into categoryItems
	    categoryItems.add(item);

	    // 3: put categoryItems into categorisedItems(hashMap) named as
	    // itemCategoryStr
	    categorisedItems.put(itemCategoryStr, categoryItems);
	}
	// 4: if category is null, return the whole categoryList
	if (category == null) {
	    List<ModelData> categoryList = new ArrayList<ModelData>();
	    for (String key : categorisedItems.keySet()) {
		categoryList.add(new Category(key));
	    }
	    return categoryList;
	} else {
	    return new ArrayList<ModelData>(categorisedItems.get(category
		    .getTitle()));
	}
    }

    @Override
    public PagingLoadResult<Item> loadItems(String feedUrl,
	    PagingLoadConfig config) {
	List<Item> items = loadItems(feedUrl);
	return getPagingLoadResult(items, config);
    }

    // take the full list of Item objects and return the page requested in the
    // PagingLoadConfig
    private PagingLoadResult<Item> getPagingLoadResult(List<Item> items,
	    PagingLoadConfig config) {
	// 定義pageItems，存儲Item，作為返回的data
	List<Item> pageItems = new ArrayList<Item>();
	// 通過PagingLoadConfig，獲得相關參數（offset）
	int offset = config.getOffset();
	// 獲得全部數據大小
	int limit = items.size();

	// 根據offset獲得limit
	if (config.getLimit() > 0)
	    limit = Math.min(offset + config.getLimit(), limit);

	// 定義好邊界之後，開始讀取數據
	for (int i = config.getOffset(); i < limit; i++)
	    pageItems.add(items.get(i));

	// 通過pageItems，轉化成BasePagingLoadResult，同時賦值上offset和totalLength
	return new BasePagingLoadResult<Item>(pageItems, offset, items.size());
    }
}
